﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;

namespace NetworkScanner
{
    class Network
    {
        public string Name { get; private set; }
        public NetworkInterface NIC { get; private set; }
        public IPAddress Addr { get; private set; }
        public IPAddress SubnetMask { get; private set; }
        public IPAddress SubnetAddr { get; private set; }
        public IPAddress GateawayAddr { get; private set; }
        public List<Host> Hosts { get; private set; }
        public uint maxHosts { get; private set; }

        /// <summary>
        /// Evento para notificar a GUI de un cambio en la coleccion de hosts
        /// </summary>
        public event NetworkEventHandler HostsChanged;

        /// <summary>
        /// Constructor simple
        /// </summary>
        public Network()
        {
            Hosts = new List<Host>();
        }

        private uint totalProgress;
        private int porcentaje, count;

        /// <summary>
        /// Metodo que inicia la exploracion en la red usando Paquetes ICMP Ping
        /// </summary>
        /// <param name="ipStart">Direccion IP inicial</param>
        /// <param name="cantidad">cantidad de direcciones IP a recorrer</param>
        public void EscanearRango(IPAddress ipStart, uint cantidad)
        {
            Hosts.Clear();
            IPAddress ipToPing;
            int timeout = 1000;
            totalProgress = cantidad * 2;
            count = 0;
            for (uint i = 0; i < cantidad; i++)
            {
                ipToPing = ipStart.Incrementar(i);
                Ping pingSender = new Ping();
                pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
                pingSender.SendAsync(ipToPing, timeout);
                count++;
                porcentaje = (int)((float)count / (float)totalProgress * 100.0);
                if (HostsChanged != null)
                    HostsChanged(this, new NetworkEventArgs(NetworkEventArgs.TipoEvento.Progreso, porcentaje));
            }
        }

        /// <summary>
        /// objeto utilizado para sincronizar acceso a la coleccion Hosts
        /// </summary>
        private Object hostslock = new Object();


        /// <summary>
        /// metodo ejecutado por el subproceso al completar la solicitud ping asincronica.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void PingCompletedCallback(object sender, PingCompletedEventArgs e)
        {
            Ping pingAsync = (Ping)sender;
            Interlocked.Increment(ref count);
            porcentaje = (int)((float)count / (float)totalProgress * 100.0);
            try
            {
                if (e.Reply.Status == IPStatus.Success)
                {
                    Host host = new Host(e.Reply.Address, e.Reply.Address.IsLAN(SubnetAddr, SubnetMask));
                    host.HostNameUpdate += new NetworkEventHandler(HostUpdate);
                    host.Latencia = e.Reply.RoundtripTime;
                    if (host.Addr.CompareTo(this.GateawayAddr) == 0)
                        host.Details = "Puerta de enlace";
                    if (host.Addr.CompareTo(this.Addr) == 0)
                        host.Details = "Este equipo";
                    lock (hostslock)
                    {
                        Hosts.Add(host);
                        Hosts.Sort();
                    }

                    if (HostsChanged != null)
                        HostsChanged(this, new NetworkEventArgs(NetworkEventArgs.TipoEvento.HostEncontrado, porcentaje));
                }
                else if (HostsChanged != null)
                    HostsChanged(this, new NetworkEventArgs(NetworkEventArgs.TipoEvento.Progreso, porcentaje));
                if ((count == totalProgress) && (HostsChanged != null))
                    HostsChanged(this, new NetworkEventArgs(NetworkEventArgs.TipoEvento.BusquedaCompletada, porcentaje));
            }
            catch (Exception) { }
            finally { pingAsync.Dispose(); }
        }

        public void HostUpdate(object sender, NetworkEventArgs e)
        {
            if (HostsChanged != null)
                HostsChanged(this, new NetworkEventArgs(NetworkEventArgs.TipoEvento.HostNameResuelto, 0));
        }


        /// <summary>
        /// Devuelve un arreglo con las redes activas (link UP) encontradas, omite loopback
        /// </summary>
        /// <returns>Arreglo con redes encontradas</returns>
        public static Network[] get_active_Networks()
        {

            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
            List<Network> Nets = new List<Network>();
            foreach (NetworkInterface netwokCard in nics)
            {
                if (netwokCard.OperationalStatus == OperationalStatus.Up
                    && netwokCard.NetworkInterfaceType != NetworkInterfaceType.Loopback)
                {
                    Network net = new Network();
                    foreach (UnicastIPAddressInformation x in netwokCard.GetIPProperties().UnicastAddresses)
                    {
                        if (x.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            net.NIC = netwokCard;
                            net.Addr = x.Address;
                            net.SubnetMask = x.IPv4Mask;
                            net.SubnetAddr = net.Addr.GetSubnetAddress(net.SubnetMask);
                            net.maxHosts = net.SubnetMask.subnetSize() - 2;
                        }
                    }
                    foreach (GatewayIPAddressInformation x in netwokCard.GetIPProperties().GatewayAddresses)
                    {
                        if (x.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {
                            net.GateawayAddr = x.Address;
                            Nets.Add(net);
                        }
                    }
                }
            }
            return Nets.ToArray();
        }




    }
}